#include "../include/Texture.hpp"
#include "../include/Libs.hpp"
#include "../include/Utils.hpp"
#include <cassert>
using namespace klas;

#include <iostream>
using namespace std;

Texture::Texture()
{
	m_textureID = INVALID_ID;
	m_displayID = INVALID_ID;
  m_pointSpriteDisplayID = INVALID_ID;
	m_isValid = false;
}

Texture::Texture(string file)
{
	load(file);
}

Texture::~Texture()
{
}

bool Texture::kill()
{
  if(glIsTexture(m_textureID))
    glDeleteTextures(1, &m_textureID);
  if(glIsList(m_displayID))
    glDeleteLists(m_displayID, 1);
  if(glIsList(m_pointSpriteDisplayID))
    glDeleteLists(m_pointSpriteDisplayID, 1);
	Texture();
  return true;
}

bool Texture::isValid() const
{
	return m_isValid;
}

iReal Texture::getObjectType() const
{
  return Data::TEXTURE;
}

GLuint Texture::getDisplayListID() const
{
	return m_displayID;
}

GLuint Texture::getPointSpriteDisplayListID() const
{
  return m_pointSpriteDisplayID;
}

GLuint Texture::getTextureID() const
{
  return m_textureID;
}

Vector2<iReal> Texture::getOrigDim() const
{
	return m_origDim;
}

Vector2<iReal> Texture::getPaddedDim() const
{
	return m_paddedDim;
}

bool Texture::load(string file)
{
	ILboolean	success;
	ILuint		ilTexID;
  
	// bind our texture
	ilGenImages(1, &ilTexID);
	ilBindImage(ilTexID);

	// attempt to load the image
	success = ilLoadImage(file.c_str());

	// if we did not load the image
	if (!success)
	{
		cout << "Texture::load error" << endl;
		ILenum Error;
		while ((Error = ilGetError()) != IL_NO_ERROR) {
				cout << Error << ": " << iluErrorString(Error) << endl;
		} 
		return false; //ilGetError();
	}

	// convert to RGBA
	success = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
	
	// if that failed? go to RGB
	if (!success)
	{
		cout << "Unable to convert to RGBA, going to RGB." << endl;
	  success = ilConvertImage(IL_RGB, IL_UNSIGNED_BYTE);
	}

	m_origDim.x = ilGetInteger(IL_IMAGE_WIDTH);
	m_origDim.y = ilGetInteger(IL_IMAGE_HEIGHT);
	
	// convert to a power of two texture
	iluImageParameter(ILU_PLACEMENT, ILU_UPPER_LEFT);
	iluEnlargeCanvas(Utils::getNextPowerOfTwo(ilGetInteger(IL_IMAGE_WIDTH)),
		 Utils::getNextPowerOfTwo(ilGetInteger(IL_IMAGE_HEIGHT)), 
		 ilGetInteger(IL_IMAGE_BPP));

	if(!success)
		assert("Texture::load was unable to convert the image to RGB(A)" && false);

	// Have OpenGL generate a texture object handle for us
	glGenTextures( 1, &m_textureID );
 	
	// Bind the texture object
	glBindTexture( GL_TEXTURE_2D, m_textureID );
 
	// Set the texture's stretching properties
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
 
	// Edit the texture object's image data using the information DevIL gives us
	glTexImage2D(GL_TEXTURE_2D, 0, ilGetInteger(IL_IMAGE_BPP), 
		ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT), 0, 
		ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE, ilGetData());

	m_paddedDim.x = ilGetInteger(IL_IMAGE_WIDTH);
	m_paddedDim.y = ilGetInteger(IL_IMAGE_HEIGHT);
	
	// Because we have already copied image data into texture data we can release 
	// memory used by DevIL.
	ilDeleteImages(1, &ilTexID); 
	
	// ------------------------------------------------------------------------------
	// We need to create the display list. ------------------------------------------
	m_displayID = glGenLists(1);
	
  //cout << glGetString(GL_EXTENSIONS) << endl;
	glNewList(m_displayID, GL_COMPILE);
		//glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
    glBindTexture(GL_TEXTURE_2D, m_textureID);
    glBegin(GL_QUADS);
			glTexCoord2i(0, 0);
			glVertex3f(0.0, 0.0, 0.0);
			
			glTexCoord2i(1, 0);
			glVertex3f(m_paddedDim.x, 0.0, 0.0);
			
			glTexCoord2i(1, 1);
			glVertex3f(m_paddedDim.x, m_paddedDim.y, 0.0);
			
			glTexCoord2i(0, 1);
      glVertex3f(0.0, m_paddedDim.y, 0.0);
    glEnd();
  glEndList();

  glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
  glNewList(m_pointSpriteDisplayID, GL_COMPILE);
    // Render using point sprites
    glEnable(GL_POINT_SPRITE);
    glBindTexture(GL_TEXTURE_2D, m_textureID);

    //glPointSize(20.0f);
    glBegin(GL_POINTS);
        glVertex2f(0, 0);
    glEnd();

    glDisable(GL_POINT_SPRITE);
  glEndList();
	
	// We are done with the display list. -------------------------------------------
	// ------------------------------------------------------------------------------
	
	m_isValid = true;
	
	return true;
}
